﻿using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using DynamicViewModel.Tests.TestTypes;
using Microsoft.CSharp.RuntimeBinder;
using Xunit;

namespace DynamicViewModel.Tests
{
    public sealed class DynamicViewModelTest
    {
        [Fact]
        public void CheckingNonExistantPropertyThrowsRuntimeBinderException()
        {
            dynamic viewModel = new DynamicViewModel();
            Assert.Throws<RuntimeBinderException>(() =>
            {
                dynamic value = viewModel.NonExistingProperty;
            });
        }

        [Fact]
        public void CheckingAfterPropertyIsSetReturnsValue()
        {
            dynamic viewModel = new DynamicViewModel();

            viewModel.Foo = "Bar";

            Assert.Same("Bar", viewModel.Foo);
        }

        [Fact]
        public void CallingSetAddsValueToViewModel()
        {
            dynamic viewModel = new DynamicViewModel();

            ((DynamicViewModel) viewModel).Set("Foo", "Bar");
            Assert.Same("Bar", viewModel.Foo);
        }

        [Fact]
        public void SettingPropertyOnStaticProperty()
        {
            dynamic viewModel = new TestViewModel();

            ((DynamicViewModel)viewModel).Set("Foo", "Bar");
            Assert.Same("Bar", viewModel.Foo);
        }

        [Fact]
        public void SettingPropertyAddsToPrivateDictionary()
        {
            dynamic viewModel = new DynamicViewModel();

            viewModel.Foo = "Bar";

            var allFields = typeof (DynamicViewModel).GetFields(BindingFlags.Instance | BindingFlags.NonPublic);

            var fieldInfo = allFields.First(p => p.Name == "_propertyValues");

            var values = (IDictionary<string, object>) fieldInfo.GetValue(viewModel);
            Assert.True(values.ContainsKey("Foo"), "Could not find property Foo");
        }

        [Fact]
        public void SettingPropertyRaisesPropertyChanged()
        {
            var isRaised = false;
            dynamic viewModel = new DynamicViewModel();
            ((INotifyPropertyChanged)viewModel).PropertyChanged += (s, e) => { isRaised = true; };
            viewModel.Foo = "Bar";

            Assert.True(isRaised, "The PropertyChanged event was not raised");
        }

        [Fact]
        public void SettingPropertyRaisesPropertyChangedOfThatName()
        {
            var isRaised = false;

            dynamic viewModel = new DynamicViewModel();
            ((INotifyPropertyChanged)viewModel).PropertyChanged += (s, e) =>
            {
                if (e.PropertyName == "Foo")
                {
                    isRaised = true;
                }
            };
            viewModel.Foo = "Bar";

            Assert.True(isRaised, "The PropertyChanged event was not raised");
        }

        [Fact]
        public void SettingPropertyToSameValueTwiceDoesNotRaiseSecondEvent()
        {
            var eventsRaised = 0;

            dynamic viewModel = new DynamicViewModel();
            ((INotifyPropertyChanged)viewModel).PropertyChanged += (s, e) =>
            {
                if (e.PropertyName == "Foo")
                    eventsRaised++;
            };
            viewModel.Foo = "Bar";
            viewModel.Foo = "Bar";

            Assert.True(1 == eventsRaised, "Event was not raised exactly once");
        }


        [Fact]
        public void CreatingDynamicViewModelFromDynamicCopiesAllProperties()
        {
            dynamic dynamicObject = new SimpleDynamicObject();
            dynamicObject.Foo = 123;
            dynamicObject.Bar = "abc";

            dynamic viewModel = DynamicViewModelFactory.Create(dynamicObject);

            Assert.Equal(123, viewModel.Foo);
            Assert.Equal("abc", viewModel.Bar);
        }
    }
}
